﻿using System.Collections.Generic;
using System.Drawing;
using System;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using WenSkin.Json.Utilities;
using System.Linq;
using System.IO;

namespace WenSkin;

public class Ini
{
    private IniGroupCollection items;

    public Ini() { }
    public Ini(string path) : this(path, Encoding.UTF8) { }
    public Ini(string path, Encoding encoding)
    {
        Path = path;
        Encoding = encoding;
        Load(path, encoding);
    }
    public string? this[string name]
    {
        get => GetItem(name);
        set
        {
            var item = Items.SelectMany(t => t.Items)?.ToList().Find(t => t.Name?.ToLower() == name.ToLower());
            if (item == null)
            {
                AddItem(null, name, value);
            }
            else
            {
                item.Value = value;
            }
        }
    }
    public string? this[string key, string name]
    {
        get => Items[key]?.Items[name]?.Value;
        set
        {
            //获取Group
            if (Items[key] is not IniGroup group)
            {
                group = this.Items.Add(key);
            }

            if (group.Items[name] is not IniItem iniItem)
            {
                iniItem = group.Items.Add(name, value);
            }
            iniItem.Value = value;
        }
    }

    #region 公有属性

    public string FileName => System.IO.Path.GetFileName(Path);
    /// <summary>
    /// 路径
    /// </summary>
    public string Path { get; private set; }
    /// <summary>
    /// 编码方式
    /// </summary>
    public Encoding Encoding { get; private set; }
    /// <summary>
    /// 内容
    /// </summary>
    public string Text => string.Join("\r\n", Items.Select(g => g.Text));

    #endregion

    /// <summary>
    /// 加载（默认UTF8编码）
    /// </summary>
    /// <param name="path"></param>
    public void Load(string path) => Load(path, Encoding.UTF8);
    /// <summary>
    /// 加载（ 指定文件编码）
    /// </summary>
    /// <param name="path"></param>
    /// <param name="encoding"></param>
    public void Load(string path, Encoding encoding)
    {
        Items.Clear();
        if (!File.Exists(path))
            return;

        string[] texts = File.ReadAllLines(path, encoding);

        IniGroup? group = null;
        foreach (var text in texts)
        {
            //选项
            string t = text.Replace(" ", "");
            var idx = text.IndexOf("=");

            //添加组
            if (t.StartsWith("[") && t.EndsWith("]"))
            {
                group = Items.Add(text.Substring(text.IndexOf("[") + 1, text.IndexOf("]") - 1));
                continue;
            }
            else if (Items.Count == 0)
            {
                group = Items.Add(null);
            }

            //添加选项
            if (t.StartsWith("!") || t.StartsWith(";") || t.StartsWith("]") || t.StartsWith("//") || string.IsNullOrEmpty(t))
            {
                group?.Items.Add(null, text);
            }
            else if (idx == -1)
            {
                group?.Items.Add(null, text);
            }
            else if (idx == 0)
            {
                group?.Items.Add("", text);
            }
            else if (idx > 0)
            {
                group?.Items.Add(text.Substring(0, idx), text.Substring(idx + 1));
            }
        }
    }
    /// <summary>
    /// 添加注释
    /// </summary>
    /// <param name="groupName"></param>
    /// <param name="value"></param>
    public void AddRemark(string groupName, string value) => AddItem(groupName, null, value);
    /// <summary>
    /// 添加选项
    /// </summary>
    /// <param name="groupName"></param>
    /// <param name="name"></param>
    /// <param name="value"></param>
    public void AddItem(string? groupName, string? name, string? value)
    {
        var g = this.Items[groupName];
        g ??= this.Items.Add(groupName);
        g.Items.Add(name, value);
    }
    /// <summary>
    /// 获取选项
    /// </summary>
    /// <param name="groupName">组名称</param>
    /// <param name="name">名称</param>
    /// <returns></returns>
    public string? GetItem(string groupName, string name) => Items[groupName]?.Items[name]?.Value;
    /// <summary>
    /// 获取选项值
    /// </summary>
    /// <param name="name">名称</param>
    /// <returns></returns>
    public string? GetItem(string name) => Items.SelectMany(t => t.Items)?.ToList().Find(t => t.Name?.ToLower() == name.ToLower())?.Value;
    /// <summary>
    /// 保存
    /// </summary>
    public void Save()
    {
        SaveAs(Path);
    }
    /// <summary>
    /// 另存为
    /// </summary>
    /// <param name="path"></param>
    public void SaveAs(string path)
    {
        SaveAs(path, Encoding);
    }
    /// <summary>
    /// 另存为（指定文件编码）
    /// </summary>
    /// <param name="path">路径</param>
    /// <param name="encoding">文件编码</param>
    public void SaveAs(string path, Encoding encoding)
    {
        File.WriteAllText(path, Text, encoding);
    }
    /// <summary>
    /// 选项
    /// </summary>
    public IniGroupCollection Items => items ??= new IniGroupCollection(this);

    /// <summary>
    /// 组
    /// </summary>
    public class IniGroup
    {
        private readonly IniGroupCollection owner;

        private IniItemCollection? items;

        public IniGroup(IniGroupCollection owner, string? name)
        {
            this.owner = owner;
            Name = name;
        }
        public Ini Ini => this.owner.Ini;
        /// <summary>
        /// 名称
        /// </summary>
        public string? Name { get; private set; }
        /// <summary>
        /// 选项合集
        /// </summary>
        public IniItemCollection Items => items ??= new IniItemCollection(this);
        /// <summary>
        /// 内容
        /// </summary>
        public string Text => (string.IsNullOrWhiteSpace(Name) ? "" : $"[{Name}]\r\n") + string.Join("\r\n", Items.Select(x => x.Text));
    }
    /// <summary>
    /// 组合计
    /// </summary>
    public class IniGroupCollection : List<IniGroup>
    {
        private readonly Ini owner;

        public IniGroupCollection(Ini owner)
        {
            this.owner = owner;
        }
        /// <summary>
        /// Ini
        /// </summary>
        public Ini Ini => this.owner;
        /// <summary>
        /// 获取选项
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public IniGroup? this[string? name] => Find(a => a.Name?.ToUpper() == name?.ToUpper());
        /// <summary>
        /// 添加选项
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public IniGroup Add(string? name)
        {
            var g = new IniGroup(this, name);
            if (name is null)
            {
                this.Insert(0, g);
            }
            else
            {
                Add(g);
            }

            return g;
        }
    }
    /// <summary>
    /// 选项
    /// </summary>
    public class IniItem
    {
        private readonly IniItemCollection owner;

        public IniItem(IniItemCollection owner, string? name, string? value)
        {
            this.owner = owner;
            Name = name;
            Value = value;
        }
        public Ini Ini => owner.Ini;

        public IniItemCollection Owner => owner;

        public int Index => owner.IndexOf(this);
        /// <summary>
        /// 名称
        /// </summary>
        public string? Name { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public string? Value { get; set; }
        /// <summary>
        /// 文本
        /// </summary>
        public string? Text => string.IsNullOrWhiteSpace(Name) ?
            (string.IsNullOrWhiteSpace(Value) || Value.StartsWith('!') || Value.StartsWith(';') ? Value : (";" + Value)) :
            $"{Name}={Value}";
        /// <summary>
        /// 是否是注释行
        /// </summary>
        public bool IsRemarkLine => string.IsNullOrWhiteSpace(this.Name);
    }
    /// <summary>
    /// 选项合集
    /// </summary>
    public class IniItemCollection : List<IniItem>
    {
        private readonly IniGroup owner;

        public IniItemCollection(IniGroup owner)
        {
            this.owner = owner;
        }
        public Ini Ini => owner.Ini;
        public IniItem? this[string name] => Find(a => a.Name?.ToUpper() == name?.ToUpper());

        public IniItem Add(string? name, string? value)
        {
            var item = new IniItem(this, name, value);
            this.Add(item);
            return item;
        }
    }

    /// <summary>
    /// 弹出编辑窗口
    /// </summary>
    public void Show() => IniFormShow();
    /// <summary>
    /// 弹出编辑窗口
    /// </summary>
    public void IniFormShow()
    {
        var fm = new Form()
        {
            Width = 925,
            Height = 571,
            Text = $"INI可视化编辑器 - 珠海森云科技出品 - {this.FileName} - {this.Encoding.EncodingName}",
            StartPosition = FormStartPosition.CenterScreen,
        };
        fm.Controls.Add(new IniControl(this) { Dock = DockStyle.Fill });
        fm.ShowDialog();
    }


    public static StringFormat StringConters { get; set; } = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.NoWrap)
    {
        LineAlignment = StringAlignment.Center,
        Trimming = StringTrimming.EllipsisCharacter
    };
    public class IniControl : Control
    {
        private Ini Ini;
        public IniControl(Ini ini)
        {
            this.Ini = ini;
            this.Size = new Size(900, 540);
            this.BackColor = Color.White;
            Initcom();
            if (ini != null)
                Load();
        }
        private FlowLayoutPanel Flow;
        private void Initcom()
        {
            Button button = new Button()
            {
                Text = "保存 应用",
                Dock = DockStyle.Bottom,
            };
            button.Click += (s, e) =>
            {
                Ini.Save();
                MessageBox.Show("保存成功!");
            };
            Flow = new FlowLayoutPanel()
            {
                Dock = DockStyle.Fill,
                AutoScroll = true,
                FlowDirection = FlowDirection.TopDown,
                WrapContents = false,
            };
            this.Controls.Add(Flow);
            this.Controls.Add(button);
        }

        public IniItem Selected => SelectedControl.IniItem;

        public IniItemControl SelectedControl { get; private set; }
        private void Load()
        {
            foreach (var ini in Ini.Items)
            {
                var groupBox = new GroupBox()
                {
                    Text = ini.Name ?? "Default (Is not group name)",
                    ForeColor = Color.DarkOrchid,
                    AutoSize = true,
                };
                var flow = new FlowLayoutPanel()
                {
                    AutoSize = true,
                    FlowDirection = FlowDirection.TopDown,
                    Dock = DockStyle.Fill,
                };
                foreach (var item in ini.Items)
                {
                    var control = new IniItemControl(this, flow, item);
                    control.Click += (s, e) =>
                    {
                        if (this.SelectedControl != null)
                            this.SelectedControl.SelectColor = Color.White;
                        control.SelectColor = Color.DarkGray;
                        this.SelectedControl = control;
                    };
                    flow.Controls.Add(control);
                }
                groupBox.Controls.Add(flow);
                Flow.Controls.Add(groupBox);
            }
        }
    }
    public class IniItemControl : Control
    {
        private readonly IniControl owner;
        private readonly FlowLayoutPanel flow;
        private readonly IniItem iniItem;
        public IniItemControl(IniControl owner, FlowLayoutPanel flow, IniItem iniItem)
        {
            base.SetStyle(
                ControlStyles.UserPaint |
                ControlStyles.DoubleBuffer |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.ResizeRedraw |
                ControlStyles.SupportsTransparentBackColor, true);
            base.UpdateStyles();
            this.BackColor = Color.Transparent;

            this.owner = owner;
            this.flow = flow;
            this.iniItem = iniItem;
            this.Height = 30;
            this.Width = 870;
            this.Margin = new Padding(1, 0, 1, 0);
            this.Name = iniItem.Name;
            this.Text = iniItem.Text;


            menuStrip.Items.Add("注释", null, (s, e) =>
            {
                var item = new IniItem(IniItem.Owner, null, ";注释");
                this.IniItem.Owner.Insert(this.IniItem.Index, item);
                var control = new IniItemControl(owner, flow, item);
                this.flow.Controls.Add(control);
                this.flow.Controls.SetChildIndex(control, this.IniItem.Index - 1);
            });

            menuStrip.Items.Add("插入", null, (s, e) =>
            {
                var item = new IniItem(IniItem.Owner, "Name", "Value");
                this.IniItem.Owner.Insert(this.IniItem.Index, item);
                var control = new IniItemControl(owner, flow, item);
                this.flow.Controls.Add(control);
                this.flow.Controls.SetChildIndex(control, this.IniItem.Index - 1);
            });

            menuStrip.Items.Add("添加", null, (s, e) =>
            {
                var item = new IniItem(IniItem.Owner, "Name", "Value");
                this.IniItem.Owner.Add(item);
                var control = new IniItemControl(owner, flow, item);
                this.flow.Controls.Add(control);
            });

            menuStrip.Items.Add("删除", null, (s, e) =>
            {
                this.IniItem.Owner.Remove(this.IniItem);
                this.Dispose();
            });
            this.ContextMenuStrip = menuStrip;
        }

        private readonly ContextMenuStrip menuStrip = new ContextMenuStrip();
        private Color selectColor = Color.White;

        public IniItem IniItem => iniItem;

        public Color SelectColor { get => selectColor; set { selectColor = value; this.BackColor = value; } }
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            this.BackColor = SelectColor;
        }
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            this.BackColor = Color.LightGray;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            var g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;  //使绘图质量最高，即消除锯齿
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.CompositingQuality = CompositingQuality.HighQuality;


            if (IniItem.IsRemarkLine)
            {
                var valueRec = new RectangleF(0, 0, Width, Height);
                g.DrawString(IniItem.Value, Font, Brushes.Green, valueRec, StringConters);
            }
            else
            {
                var nameRec = new RectangleF(0, 0, 145, Height);
                var valueRec = new RectangleF(150, 0, Width - 150, Height);
                g.DrawString(IniItem.Name, Font, Brushes.DodgerBlue, nameRec, StringConters);
                g.DrawString(IniItem.Value, Font, Brushes.Black, valueRec, StringConters);
            }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            if (IniItem.IsRemarkLine)
            {
                ShowTextBox(Width, 0);
            }
            else
            {
                if (new RectangleF(0, 0, 145, Height).Contains(e.Location))
                {
                    ShowTextBox(145, 0, false);
                }
                else
                {
                    ShowTextBox(Width - 150, 150);
                }
            }
        }

        private void ShowTextBox(int width, int x, bool isValue = true)
        {
            var tb = new TextBox()
            {
                Width = width,
                Text = isValue ? IniItem.Value : IniItem.Name,
                ForeColor = IniItem.IsRemarkLine ? Color.Green : Color.Black,
            };
            tb.Location = new Point(x, (this.Height - tb.Height) / 2);

            tb.LostFocus += (s, e) =>
            {
                tb.Dispose();
            };
            tb.TextChanged += (s, e) =>
            {
                if (isValue)
                    IniItem.Value = tb.Text;
                else
                    IniItem.Name = tb.Text;
            };
            tb.KeyDown += (s, e) =>
            {
                if (e.KeyCode == Keys.Enter)
                {
                    tb.Dispose();
                }
            };
            this.Controls.Add(tb);
            tb.Select();
        }
    }
}
